package jee.boot.module.oauth2.service;

import jee.boot.common.basic.CrudService;
import jee.boot.common.basic.ServiceException;
import jee.boot.common.basic.result.MapData;
import jee.boot.common.basic.result.MessageCodeHelper;
import jee.boot.entity.auth.Account;
import jee.boot.entity.system.Filter;
import jee.boot.common.standard.AccountHandler;
import jee.boot.common.utils.*;
import jee.boot.entity.mail.Mail;
import jee.boot.entity.sms.Sms;
import jee.boot.module.oauth2.dao.AccountDao;
import jee.boot.module.oauth2.dto.AccountDto;
import jee.boot.entity.auth.AuthConstant;
import jee.boot.entity.auth.AccountHistoryPassword;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * oauth 认证账户
 */
@Service
public class AccountService extends CrudService<AccountDao, Account> {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private AccountHandler accountHandler;
    @Autowired
    private AccountHistoryPasswordService accountHistoryPasswordService;
    @Autowired
    private MessageCodeHelper messageCodeHelper;
//    @Autowired
//    private SendMailHelper sendMailHelper;
//    @Autowired
//    private SendSmsHelper sendSmsHelper;

    @Override
    public void save(Account entity) {
        if (StringUtils.isNotBlank(entity.getNewPassword())) {
            entity.setPassword(encryptionPassword(entity.getNewPassword()));
        } else {
            entity.setPassword(encryptionPassword(DigestUtils.md5DigestAsHex(AuthConstant.DEFAULT_RESET_PASSWORD.getBytes())));
        }
        check(entity);
        super.save(entity);
    }

    @Override
    public void update(Account entity) {
        check(entity);
        super.update(entity);
    }

    /**
     * 根据登录名/邮箱/电话获取登录用户
     *
     * @param entity
     * @return
     */
    public Account findListForLogin(Account entity) {
        entity.setStatus("");//如果需要查询出status字段,则需要给status设置一个不为null的值
        entity.setPassword("");
        entity.addFilter(new Filter(new Filter("username", "=", entity.getUsername()), "or", new Filter(new Filter("email", "=", entity.getUsername()), "or", new Filter("phone", "=", entity.getUsername()))));
        List<Account> list = super.findList(entity);
        if (list != null && !list.isEmpty()) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 更改密码
     *
     * @param entity
     */
    public void updatePassword(Account entity) {
        String oldPassword = entity.getOldPassword();
        String newPassword = entity.getNewPassword();
        entity.setId(accountHandler.getPrincipal().getId());
        entity.setPassword("");
        if (StringUtils.isNotBlank(oldPassword) && StringUtils.isNotBlank(newPassword)) {
            Account account = get(entity);
            // 验证旧密码是否正确
            if (validatePassword(oldPassword, account.getPassword())) {
                setNewPassword(entity);
            } else {
                throw new ServiceException(1102);
            }
        } else {
            throw new ServiceException(1103);
        }
    }

    private void setNewPassword(Account entity) {
        // 验证是历史密码中是否包含当前密码
        if (accountHistoryPasswordService.checkPasswordRepeated(entity)) {
            throw new ServiceException(1101);
        }
        entity.setPassword(encryptionPassword(entity.getNewPassword()));
        entity.setAccountStatus("account_status_1");
        entity.setPasswordInvalidDate(DateUtils.addDays(new Date(), 30));
        dao.updatePassword(entity);
        AccountHistoryPassword historyPassword = new AccountHistoryPassword();
        // 保存历史密码
        historyPassword.setAccount(entity);
        historyPassword.setPassword(entity.getPassword());
        accountHistoryPasswordService.save(historyPassword);
    }

    /**
     * 检查登录账户是否重复
     *
     * @param entity
     */
    public void check(Account entity) {
        if (StringUtils.isBlank(entity.getUsername()) && StringUtils.isBlank(entity.getEmail()) && StringUtils.isBlank(entity.getPhone())) {
            throw new ServiceException(1104);
        }
        if (StringUtils.isNotBlank(entity.getUsername())) {
            checkValue(entity, entity.getUsername());
        }
        if (StringUtils.isNotBlank(entity.getEmail())) {
            checkValue(entity, entity.getEmail());
        }
        if (StringUtils.isNotBlank(entity.getPhone())) {
            checkValue(entity, entity.getPhone());
        }
    }

    private void checkValue(Account entity, String value) {
        Account account = new Account();
        account.addFilter(new Filter(new Filter("username", "=", value), "or", new Filter(new Filter("email", "=", value), "or", new Filter("phone", "=", value))));
        List<Account> list = super.findList(account);
        if (list != null && !list.isEmpty()) {
            for (Account u : list) {
                if (!StringUtils.equals(entity.getId(), u.getId())) {
                    throw new ServiceException(1105, MapData.instance().put("username", value).data());
                }
            }
        }
    }

    /**
     * 加密
     */
    public static String encryptionPassword(String plainPassword) {
        return new BCryptPasswordEncoder().encode(plainPassword);
    }

    /**
     * 验证密码
     *
     * @param plainPassword 明文密码
     * @param password      密文密码
     * @return 验证成功返回true
     */
    public static boolean validatePassword(String plainPassword, String password) {
        return new BCryptPasswordEncoder().matches(plainPassword, password);
    }

    /**
     * 重置密码
     *
     * @param entity
     */
    public void resetPassword(Account entity) {
        entity.setPassword(encryptionPassword(DigestUtils.md5DigestAsHex(AuthConstant.DEFAULT_RESET_PASSWORD.getBytes())));
        dao.updatePassword(entity);
    }

    public static void main(String[] args) {
        System.out.println(encryptionPassword(DigestUtils.md5DigestAsHex("system".getBytes())));
    }

    /**
     * 获取账户的验证方式
     *
     * @param entity
     * @return
     */
    public AccountDto getAccountVerificationMethod(AccountDto entity) {
        String pin = entity.getPin();
        String pinKey = entity.getPinKey();
        boolean checkCode = VerificationCodeUtil.checkCode(pinKey, pin);
        if (!checkCode) {
            throw new ServiceException(1113);
        }

        Account account = new Account();
        account.setUsername(entity.getUsername());
        Account login = findListForLogin(account);
        if (login == null) {
            throw new ServiceException(1117);
        }
        AccountDto accountDto = new AccountDto();
        accountDto.setId(login.getId());
        // 信息脱敏
        if (StringUtils.isNotBlank(login.getPhone())) {
            accountDto.setPhone(SensitiveInfoUtils.mobilePhone(login.getPhone()));
        }
        if (StringUtils.isNotBlank(login.getEmail())) {
            accountDto.setEmail(SensitiveInfoUtils.email(login.getEmail()));
        }
        return accountDto;
    }

    /**
     * 发送验证码信息
     *
     * @param entity
     * @return
     */
    public AccountDto sendCode(AccountDto entity) {
        Account login = get(new Account(entity.getId()));
        if (login == null) {
            throw new ServiceException(1117);
        }
        // create one number code .
        String code = VerificationCodeUtil.getVerificationNum(6);
        boolean sendFlag = false;
        switch (entity.getType()) {
            case "phone":
                Sms sms = createSms(login, code);
                sms.setAsyn(false);//同步发送
                remoteEndpoint.executePost(projectConfig.getOpenMessageServer(), "/sms/send", sms, Sms.class);
                if (StringUtils.equals("Y", sms.getSendStatus())) {
                    sendFlag = true;
                }
                break;
            case "email":
                Mail mail = createMail(login, code);
                mail.setAsyn(false);//同步发送
                remoteEndpoint.executePost(projectConfig.getOpenMessageServer(), "/mail/send", mail, Mail.class);
                if (StringUtils.equals("Y", mail.getSendStatus())) {
                    sendFlag = true;
                }
                break;
        }
        String s = VerificationCodeUtil.cacheCode(code);
        entity.setPinKey(s);
        if (!sendFlag) {
            throw new ServiceException(1116);
        }
        return entity;
    }

    private Sms createSms(Account login, String code) {
        Sms sms = new Sms();
        sms.setPhoneNumber(login.getPhone());
        sms.setSignName(messageCodeHelper.getMessage(1438));
        sms.setTemplateCode(messageCodeHelper.getMessage(1436));
        sms.setTemplateParam(JsonMapper.toJsonString(MapData.instance().put("code", code).data()));
        return sms;
    }

    private Mail createMail(Account login, String code) {
        Map<String, Object> data = new HashMap<>();
        // 同步发送邮件
        Mail mail = new Mail();
        mail.setToMail(login.getEmail());
        mail.setTemplateName("login");
        mail.setSubject(messageCodeHelper.getMessage(1434));
        data.put("code", code);
        data.put("account", login);
        mail.setData(data);
        return mail;
    }

    /**
     * 检查code是否有正确,如果正确,则返回一个ticket,用于对重置密码的请求进行校验
     *
     * @param entity
     * @return
     */
    public AccountDto checkCode(AccountDto entity) {
        String pin = entity.getPin();
        String pinKey = entity.getPinKey();
        boolean checkCode = VerificationCodeUtil.checkCode(pinKey, pin);
        if (!checkCode) {
            throw new ServiceException(1113);
        }
        // 生成一个凭证
        String ticket = VerificationCodeUtil.getVerificationCode(64);
        String s = VerificationCodeUtil.cacheCode(ticket);
        entity.setPin(ticket);
        entity.setPinKey(s);
        return entity;
    }

    /**
     * 重置新密码
     *
     * @param entity
     */
    public void resetNewPassword(AccountDto entity) {
        String pin = entity.getPin();
        String pinKey = entity.getPinKey();
        boolean checkCode = VerificationCodeUtil.checkCode(pinKey, pin, false);
        if (!checkCode) {
            throw new ServiceException(1113);
        }
        Account account = new Account(entity.getId());
        account.setNewPassword(entity.getPassword());
        setNewPassword(account);
        VerificationCodeUtil.removeCacheKey(pinKey);
    }
}
